package io.github.novaframe.web.upload;

import io.github.novaframe.common.util.MD5Util;
import org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.util.Date;

public class FileUploadUtil {
	
	//默认大小 50M
    public static final long DEFAULT_MAX_SIZE = 52428800;

    //默认上传的地址
    private static String defaultBaseDir = "upload";
    private static boolean absoluteBaseDir = false;
    private static String accessBaseUrl = "";

    //默认的文件名最大长度
    public static final int DEFAULT_FILE_NAME_LENGTH = 200;

    public static final String[] IMAGE_EXTENSION = {
        "bmp", "gif", "jpg", "jpeg", "png"
    };

    public static final String[] FLASH_EXTENSION = {
        "swf", "flv"
    };

    public static final String[] MEDIA_EXTENSION = {
        "swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg", "asf", "rm", "rmvb"
    };

    public static final String[] DEFAULT_ALLOWED_EXTENSION = {
        //图片
        "bmp", "gif", "jpg", "jpeg", "png",
        //word excel powerpoint
        "doc", "docx", "xls", "xlsx", "ppt", "pptx",
        "html", "htm", "txt",
        //压缩文件
        "rar", "zip", "gz", "bz2",
        //pdf
        "pdf",
        // 音频
        "mp3", "wav", "aac", "m4a"
    };

    private static int counter = 0;

    public static void setDefaultBaseDir(String defaultBaseDir) {
        FileUploadUtil.defaultBaseDir = defaultBaseDir;
    }

    public static String getDefaultBaseDir() {
        return defaultBaseDir;
    }

    public static boolean isAbsoluteBaseDir() {
        return absoluteBaseDir;
    }

    public static void setAbsoluteBaseDir(boolean absoluteBaseDir) {
        FileUploadUtil.absoluteBaseDir = absoluteBaseDir;
    }

    public static String getAccessBaseUrl() {
        return accessBaseUrl;
    }

    public static void setAccessBaseUrl(String accessBaseUrl) {
        FileUploadUtil.accessBaseUrl = accessBaseUrl;
    }

    public static final File localUpload(
            HttpServletRequest request,
            String relativeDir,
            File file,
            boolean needDatePathAndRandomName) throws FileSizeLimitExceededException, IOException {

        int fileNamelength = file.getName().length();
        if (fileNamelength > FileUploadUtil.DEFAULT_FILE_NAME_LENGTH) {
            throw new FileUploadException("file name is large than default.");
        }

        String filename = file.getName();
        int slashIndex = filename.indexOf("/");
        if (slashIndex >= 0) {
            filename = filename.substring(slashIndex + 1);
        }

        if(needDatePathAndRandomName) {
            filename = datePath() + File.separator + encodingFilename(filename);
        }

        // TODO relativeDir标准处理
        if (StringUtils.isNotBlank(filename)) {
            filename = relativeDir + File.separator + filename;
        }

        String absoluteBaseDir = extractUploadDir(request);
        File desc = getAbsoluteFile(absoluteBaseDir, filename);

        FileInputStream fileInputStream = new FileInputStream(file);
        FileOutputStream fileOutputStream = new FileOutputStream(desc);

        IOUtils.copy(fileInputStream, fileOutputStream);
        fileInputStream.close();
        fileOutputStream.close();
        return desc;
    }

    /**
     * 以默认配置进行文件上传
     *
     * @param request 当前请求
     * @param file    上传的文件
     * @param result  添加出错信息
     * @return
     */
    public static final File upload(HttpServletRequest request, String relativeDir, MultipartFile file, BindingResult result) {
        return upload(request, relativeDir, file, result, DEFAULT_ALLOWED_EXTENSION);
    }

    /**
     * 以默认配置进行文件上传
     *
     * @param request          当前请求
     * @param file             上传的文件
     * @param result           添加出错信息
     * @param allowedExtension 允许上传的文件类型
     * @return
     */
	public static final File upload(HttpServletRequest request, String relativeDir, MultipartFile file, BindingResult result,
			String[] allowedExtension) {
		try {
			return upload(request, getDefaultBaseDir(), relativeDir, file, allowedExtension, DEFAULT_MAX_SIZE, true);
		} catch (FileSizeLimitExceededException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return null;
	}

    /**
     * 文件上传
     *
     * @param request          当前请求 从请求中提取 应用上下文根
     * @param baseDir          文件上传的根目录
     * @param relativeDir      文件上传的相对目录
     * @param file             上传的文件
     * @param allowedExtension 允许的文件类型 null 表示允许所有
     * @param maxSize          最大上传的大小 -1 表示不限制
     * @param needDatePathAndRandomName 是否需要日期目录和随机文件名前缀
     * @return 返回上传成功的文件名
     * @throws InvalidExtensionException      如果MIME类型不允许
     * @throws FileSizeLimitExceededException 如果超出最大大小
     * @throws FileNameLengthLimitExceededException
     *                                        文件名太长
     * @throws IOException                    比如读写文件出错时
     */
    public static final File upload(
            HttpServletRequest request, 
            String baseDir,
            String relativeDir,
            MultipartFile file,
            String[] allowedExtension, 
            long maxSize, 
            boolean needDatePathAndRandomName) throws FileSizeLimitExceededException, IOException {

        int fileNamelength = file.getOriginalFilename().length();
        if (fileNamelength > FileUploadUtil.DEFAULT_FILE_NAME_LENGTH) {
        	throw new FileUploadException("file name is large than default.");
        }

        assertAllowed(file, allowedExtension, maxSize);
        String filename = extractFilename(relativeDir, file, needDatePathAndRandomName);

        String absoluteBaseDir = extractUploadDir(request);
        File desc = getAbsoluteFile(absoluteBaseDir, filename);

        file.transferTo(desc);
        return desc;
    }

    private static final File getAbsoluteFile(String uploadDir, String filename) throws IOException {

        uploadDir = FilenameUtils.normalizeNoEndSeparator(uploadDir);

        File desc = new File(uploadDir + File.separator + filename);

        if (!desc.getParentFile().exists()) {
            desc.getParentFile().mkdirs();
        }
        //
        //if (!desc.exists()) {
        //    desc.createNewFile();
        //}
        return desc;
    }

    public static final String extractFilename(String relativeDir, MultipartFile file, boolean needDatePathAndRandomName)
            throws UnsupportedEncodingException {
        String filename = file.getOriginalFilename();
        int slashIndex = filename.indexOf("/");
        if (slashIndex >= 0) {
            filename = filename.substring(slashIndex + 1);
        }

        if(needDatePathAndRandomName) {
            filename = datePath() + File.separator + encodingFilename(filename);
        }

        // TODO relativeDir标准处理
        if (StringUtils.isNotBlank(filename)) {
            filename = relativeDir + File.separator + filename;
        }

        return filename;
    }

    /**
     * 编码文件名,默认格式为：
     * 1、'_'替换为 ' '
     * 2、hex(md5(filename + now nano time + counter++))
     * 3、[2]_原始文件名
     *
     * @param filename
     * @return
     */
    private static final String encodingFilename(String filename) {
        //filename = filename.replace("_", " ");
        filename = MD5Util.MD5Encode("" + System.nanoTime() + counter++) + "_" + filename;
        return filename;
    }

    /**
     * 日期路径 即年/月/日  如2013/01/03
     *
     * @return
     */
    private static final String datePath() {
        Date now = new Date();
        return DateFormatUtils.format(now, "yyyy/MM/dd");
    }

    /**
     * 是否允许文件上传
     *
     * @param file             上传的文件
     * @param allowedExtension 文件类型  null 表示允许所有
     * @param maxSize          最大大小 字节为单位 -1表示不限制
     * @return
     * @throws InvalidExtensionException      如果MIME类型不允许
     * @throws FileSizeLimitExceededException 如果超出最大大小
     */
    public static final void assertAllowed(MultipartFile file, String[] allowedExtension, long maxSize)
            throws FileSizeLimitExceededException {

        String filename = file.getOriginalFilename();
        String extension = FilenameUtils.getExtension(file.getOriginalFilename());

        if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) {
            if (allowedExtension == IMAGE_EXTENSION) {
            	throw new FileUploadException("Image Tpye Extension: " + filename);
            } else if (allowedExtension == FLASH_EXTENSION) {
            	throw new FileUploadException("Flash Tpye Extension: " + filename);
            } else if (allowedExtension == MEDIA_EXTENSION) {
            	throw new FileUploadException("Media Tpye Extension: " + filename);
            } else {
            	throw new FileUploadException("Tpye Extension: " + filename);
            }
        }

        long size = file.getSize();
        if (maxSize != -1 && size > maxSize) {
            throw new FileSizeLimitExceededException("not allowed upload", size, maxSize);
        }
    }

    /**
     * 判断MIME类型是否是允许的MIME类型
     *
     * @param extension
     * @param allowedExtension
     * @return
     */
    public static final boolean isAllowedExtension(String extension, String[] allowedExtension) {
        for (String str : allowedExtension) {
            if (str.equalsIgnoreCase(extension)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 提取上传的根目录 默认是应用的根
     *
     * @param request
     * @return
     */
    public static final String extractUploadDir(HttpServletRequest request) {
        if (absoluteBaseDir) {
            return FileUploadUtil.getDefaultBaseDir();
        } else {
            return request.getServletContext().getRealPath("/") + File.separator + FileUploadUtil.getDefaultBaseDir();
        }
    }

    public static final void delete(HttpServletRequest request, String filename) throws IOException {
        if (StringUtils.isEmpty(filename)) {
            return;
        }
        File desc = getAbsoluteFile(extractUploadDir(request), filename);
        if (desc.exists()) {
            desc.delete();
        }
    }

    public static final String getRelativePath(File file) {
        String relativePath = "";
        if (file != null && file.exists()) {
            String path = file.getAbsolutePath();
            if (absoluteBaseDir) {
                relativePath = StringUtils.substring(path, StringUtils.length(defaultBaseDir));
            } else {
                relativePath = StringUtils.substring(path, path.indexOf(defaultBaseDir) + StringUtils.length(defaultBaseDir));
            }

            relativePath = StringUtils.replace(relativePath, "\\", "/");
            if (StringUtils.startsWith(relativePath, "/")) {
                relativePath = StringUtils.substring(relativePath, 1);
            }
        }

        return relativePath;
    }

}
